#include "limbo/arp/state.hpp"
#include "test-utils.h"

using namespace limbo;

uint8_t src_raw[] = {0xbc, 0xa8, 0xa6, 0x83, 0x53, 0x24};
uint8_t dst_raw[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

auto ip_src = make_address("192.168.100.15");
auto ip_dst = make_address("192.168.100.3");
auto mac_src = ethernet::MacAddress(src_raw);
auto mac_dst = ethernet::MacAddress(dst_raw);

using State = arp::State<void>;
using Context = typename State::Context;
using Packet = typename State::Packet;

TEST_CASE("send", "[arp]") {
  uint8_t example_raw[] = {
      0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xbc, 0xa8,
      0xa6, 0x83, 0x53, 0x24, 0xc0, 0xa8, 0x64, 0x0f, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x64, 0x03,
  };
  auto example_chunk = Chunk(example_raw, sizeof(example_raw));

  char buff[sizeof(example_raw)];
  auto buff_chunk = Chunk(buff, sizeof(example_raw));
  auto ctx = Context{ip_src, ip_dst, mac_src, mac_dst, arp::Operation::request,
                     nullptr};
  auto state = State();

  auto result = Packet::send(state, ctx, buff_chunk, {});
  REQUIRE(result);
  auto res_buff = result.consumed();
  CHECK((void *)res_buff.data() == (void *)buff);
  CHECK(res_buff.size() == example_chunk.size());
  CHECK(res_buff == example_chunk);
}

TEST_CASE("recv/success", "[arp]") {
  uint8_t example_raw[] = {
      0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xbc, 0xa8,
      0xa6, 0x83, 0x53, 0x24, 0xc0, 0xa8, 0x64, 0x0f, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x64, 0x03,
  };
  auto example_chunk = Chunk(example_raw, sizeof(example_raw));

  auto state = State();
  auto result = state.recv(example_chunk, nullptr);
  REQUIRE(result);
  CHECK(result.consumed() == example_chunk);
  CHECK(result.state() == &state);
  auto &packet = state.get_parsed();
  CHECK(packet.sender_mac == mac_src);
  CHECK(packet.target_mac == mac_dst);
  CHECK(packet.sender_ip == ip_src);
  CHECK(packet.target_ip == ip_dst);
  CHECK(packet.target_ip == ip_dst);
  CHECK(packet.operation == (uint16_t)arp::Operation::request);
}
